package org.sky.logger.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.sky.logger.mdc.LogMdc;
import org.sky.logger.util.Constant;
import org.sky.logger.util.Util;
import org.sky.logger.util.shiro.AntPathMatcher;
import org.sky.logger.util.shiro.PatternMatcher;
import org.sky.logger.web.LogRequestWrapper;
import org.sky.logger.web.LogResponseWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 日志过滤器
 * 
 * @author gaofu
 *
 */
public class LogFilter implements Filter {
	private static final Logger logger = LoggerFactory.getLogger(LogFilter.class);

	/**
	 * 单条日志最大长度
	 */
	private int maxLength = 10000;

	/**
	 * 匹配的uri
	 */
	private String uriPattern = null;

	private String[] uriPatterns = null;

	/**
	 * 排除匹配的uri
	 */
	private String excludeUriPattern = null;

	private String[] excludeUriPatterns = null;

	private volatile AntPathMatcher matcher = null;

	private Object lock = new Object();

	public void init(FilterConfig filterConfig) throws ServletException {
		setUriPattern(Util.delBlank(filterConfig.getInitParameter("uri-pattern")));
		setExcludeUriPattern(Util.delBlank(filterConfig.getInitParameter("exclude-uri-pattern")));
		String maxLengthParam = Util.delBlank(filterConfig.getInitParameter("maxLength"));
		if (!Util.isEmpty(maxLengthParam)) {
			maxLength = Integer.valueOf(maxLengthParam);
		}
	}

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) req;
		HttpServletResponse httpResponse = (HttpServletResponse) res;
		String servletPath = getServletPath(httpRequest);
		// 是否匹配
		boolean matching = matching(servletPath);
		// 不匹配
		if (!matching) {
			chain.doFilter(req, res);
		} else {
			String remoteAddr = httpRequest.getRemoteAddr();
			String point = servletPath;
			LogRequestWrapper request = new LogRequestWrapper(httpRequest);
			LogResponseWrapper response = new LogResponseWrapper(httpResponse);
			try {
				String logid = httpRequest.getHeader(Constant.LogId);
				if (Util.isEmpty(logid)) {
					logid = httpRequest.getParameter(Constant.LogId);
					if (Util.isEmpty(logid)) {
						logid = Util.logId();
						remoteAddr = "127.0.0.1";
					}
				}
				String appName = httpRequest.getHeader(Constant.AppName);
				if (Util.isEmpty(appName)) {
					appName = httpRequest.getParameter(Constant.AppName);
				}
				if (!Util.isEmpty(appName)) {
					remoteAddr = appName + " " + remoteAddr;
				}
				LogMdc.init(logid, point);
				// 请求报文日志
				requestLog(remoteAddr, point, request);
			} catch (Throwable t) {
				logger.error(point + "请求报文日志异常", t);
			}
			try {
				chain.doFilter(request, response);
			} finally {
				try {
					res.getOutputStream().write(response.getContentAsByteArray());
					// 响应报文日志
					responseLog(remoteAddr, point, response);
					LogMdc.clear();
				} catch (Throwable t) {
					logger.error(point + "响应报文日志异常", t);
				}
			}
		}
	}

	public void destroy() {

	}

	/**
	 * 获取servletPath
	 * 
	 * @return
	 */
	public String getServletPath(HttpServletRequest request) {
		String contextPath = request.getContextPath();
		String uri = request.getRequestURI();
		String servletPath = uri;
		if (!Util.isEmpty(contextPath)) {
			servletPath = uri.substring(contextPath.length());
		}
		return servletPath;
	}

	/**
	 * uri 是否匹配
	 * 
	 * @param uri
	 * @return
	 */
	public boolean matching(String uri) {
		// 是否匹配
		boolean matching = !excludeUriPattern(uri) && (defaultUriPattern(uri) || uriPattern(uri));
		return matching;
	}

	/**
	 * 默认匹配的uri
	 * 
	 * @param httpRequest
	 * @return
	 */
	public boolean defaultUriPattern(String uri) {
		if (!Util.isEmpty(uri)) {
			// 最后的 / 后面没有 . 则返回真
			int i = uri.lastIndexOf("/");
			int poit = uri.lastIndexOf(".");
			if (poit < i && i < uri.length() - 1) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 包含匹配的uri
	 * 
	 * @param httpRequest
	 * @return
	 */
	public boolean uriPattern(String uri) {
		if (!Util.isEmpty(uriPatterns)) {
			boolean matching = false;
			for (int i = 0; i < uriPatterns.length; ++i) {
				String pattern = uriPatterns[i];
				matching = patternMatcher().matches(pattern, uri);
				if (matching) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 排除匹配的uri
	 * 
	 * @param httpRequest
	 * @return
	 */
	public boolean excludeUriPattern(String uri) {
		if (!Util.isEmpty(excludeUriPatterns)) {
			boolean matching = false;
			for (int i = 0; i < excludeUriPatterns.length; ++i) {
				String pattern = excludeUriPatterns[i];
				matching = patternMatcher().matches(pattern, uri);
				if (matching) {
					return true;
				}
			}
		}
		return false;
	}

	public PatternMatcher patternMatcher() {
		if (matcher == null) {
			synchronized (lock) {
				if (matcher == null) {
					matcher = new AntPathMatcher();
					matcher.setPathSeparator("");
				}
			}
		}
		return matcher;
	}

	/**
	 * 单条日志最大长度
	 * 
	 * @param log
	 * @return
	 */
	protected String maxLog(String log) {
		int max = getMaxLength();
		if (log != null && log.length() > max) {
			log = log.substring(0, max) + "...";
		}
		return log;
	}

	/**
	 * 打印请求报文日志
	 * 
	 * @param servletPath
	 * @param request
	 */
	protected void requestLog(String remoteAddr, String servletPath, LogRequestWrapper request) {
		String requestBody = "null";
		try {
			// 获取内容
			requestBody = maxLog(request.getContent());
		} catch (Throwable e) {
			requestBody = "ERROR";
			logger.error("读取请求报文异常", e);
		}
		logger.info(remoteAddr + " " + servletPath + " begin ==>  " + requestBody);
	}

	/**
	 * 打印响应报文日志
	 * 
	 * @param servletPath
	 * @param response
	 */
	protected void responseLog(String remoteAddr, String servletPath, LogResponseWrapper response) {
		String responseBody = "null";
		try {
			// 获取内容
			responseBody = maxLog(response.getContent());
		} catch (Throwable e) {
			responseBody = "ERROR";
			logger.error("读取响应报文异常", e);
		}
		logger.info(remoteAddr + " " + servletPath + " end <==  " + responseBody);
	}

	public int getMaxLength() {
		return maxLength;
	}

	public void setMaxLength(int maxLength) {
		this.maxLength = maxLength;
	}

	public String getUriPattern() {
		return uriPattern;
	}

	public void setUriPattern(String uriPattern) {
		this.uriPattern = uriPattern;
		if (Util.isEmpty(uriPattern)) {
			uriPatterns = null;
		} else {
			uriPatterns = uriPattern.split("\\|");
		}
	}

	public String getExcludeUriPattern() {
		return excludeUriPattern;
	}

	public void setExcludeUriPattern(String excludeUriPattern) {
		this.excludeUriPattern = excludeUriPattern;
		if (Util.isEmpty(excludeUriPattern)) {
			excludeUriPatterns = null;
		} else {
			excludeUriPatterns = excludeUriPattern.split("\\|");
		}
	}

	public String[] getUriPatterns() {
		return uriPatterns;
	}

	public void setUriPatterns(String[] uriPatterns) {
		this.uriPatterns = uriPatterns;
		if (Util.isEmpty(uriPatterns)) {
			uriPattern = null;
		} else {
			uriPattern = Util.arrayToString(uriPatterns, "|");
		}
	}

	public String[] getExcludeUriPatterns() {
		return excludeUriPatterns;
	}

	public void setExcludeUriPatterns(String[] excludeUriPatterns) {
		this.excludeUriPatterns = excludeUriPatterns;
		if (Util.isEmpty(excludeUriPatterns)) {
			excludeUriPattern = null;
		} else {
			excludeUriPattern = Util.arrayToString(excludeUriPatterns, "|");
		}
	}

	public AntPathMatcher getMatcher() {
		return matcher;
	}

	public void setMatcher(AntPathMatcher matcher) {
		this.matcher = matcher;
	}

}